/*
 * Copyright 2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.pajamas.example.client.starter.core.service;

import org.apache.commons.collections4.CollectionUtils;
import org.pajamas.daydream.template.sequence.annotation.SequenceBinding;
import org.pajamas.daydream.template.sequence.annotation.SequenceKey;
import org.pajamas.example.client.starter.core.entity.AlbumEntity;
import org.pajamas.example.client.starter.core.repo.AlbumRepo;
import org.pajamas.starter.core.service.ClusterSynchronized;
import org.pajamas.starter.core.service.SelfProxyAware;
import org.pajamas.starter.core.service.WithoutTx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

/**
 * @author william
 * @since 2024/1/9
 */
@Service
@Slf4j
public class AlbumService implements SelfProxyAware<AlbumService> {
    private AlbumService selfProxy;

    @Autowired
    private AlbumRepo repo;

    public void renewTimestamp() {
        this.doRenewTimestamp();
    }

    public void findRenewTimestamp() {
        this.doRenewTimestamp();
    }

    @WithoutTx
    public void noRenewTimestamp() {
        this.doRenewTimestamp();
    }

    private void doRenewTimestamp() {
        List<AlbumEntity> entities = this.repo.findAll();
        if (CollectionUtils.isNotEmpty(entities)) {
            for (AlbumEntity entity : entities) {
                entity.setCreateDate(new Date());
            }
            this.repo.saveAll(entities);
        }
    }

    @Async
    public void aBusyTask(int taskNo, CountDownLatch latch) throws InterruptedException {
        log.info("aBusyTask.taskNo:[{}] beginning ...", taskNo);
        TimeUnit.SECONDS.sleep(2);
        log.info("aBusyTask.taskNo:[{}] done !!!", taskNo);
        latch.countDown();
    }

    @Async
    public void aThrowableTask() throws SQLException {
//        throw new RuntimeException("what a runtime exception");
        throw new SQLException("what a sql exception");
    }

    @Async
    public void aLogTask(int taskNo, CountDownLatch latch) {
        log.info("aLogTask.taskNo:[{}] beginning ...", taskNo);
        log.info("aLogTask.taskNo:[{}] done !!!", taskNo);
        latch.countDown();
    }

    @Override
    public AlbumService getProxy() {
        return this.selfProxy;
    }

    @Override
    public void setProxy(AlbumService proxy) {
        this.selfProxy = proxy;
    }

    @Cacheable("album-namespace")
    public List<AlbumEntity> queryEntities(int ver) {
        return this.repo.findByVerEquals(ver);
    }

    // @Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
    public void logAfter5Sec() {
        long ms = System.currentTimeMillis();
        log.error("logAfter5Sec.ms:[{}]", ms);
    }

    // @Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
    public void throwAfter5Sec() throws SQLException {
        long ms = System.currentTimeMillis();
        log.error("throwAfter5Sec.ms:[{}]", ms);
        throw new SQLException("throwAfter5Sec throw sql ex");
    }

    public String testSequenceBinding(SequenceBinding binding, @SequenceKey(key = "year") int key2,
                                      @SequenceKey(key = "name") String key1) {
        return binding.getSequenceString();
    }

    @ClusterSynchronized
    public String testClusterSynchronized(SequenceBinding binding, @SequenceKey(key = "year") int key2,
                                          @SequenceKey(key = "name") String key1) {
        return binding.getSequenceString();
    }

    @ClusterSynchronized
    public String testClusterSynchronizedWithoutBinding(int key2, String key1) {
        return key1;
    }

    @ClusterSynchronized(pattern = "test_{}_key_{}", fair = true)
    public String testClusterSynchronizedWithPattern(@SequenceKey int key2, @SequenceKey String key1) {
        return key1;
    }
}

